﻿using System;
using System.Runtime.InteropServices;
using System.IO;
using CEED_Mini;

namespace CEED.Utils
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ExeFSHeaderEntry
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] name;
        public uint offset;
        public uint size;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct HashData
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
        public byte[] Data;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ExeFSHeader
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public ExeFSHeaderEntry[] HeaderEntries;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
        public byte[] Reversed;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public HashData[] HashList;
    }

    public class ExeFS
    {
        public ExeFSHeader Header;
        public byte[] data;

        public ExeFS(Stream stream)
        {
            Header = StructReader.ReadStruct<ExeFSHeader>(stream);
            int datasize = 0;
            for (int i = 0; i < 10; i++)
            {
                datasize += (int)Header.HeaderEntries[i].size;
            }
            data = new byte[datasize];
            stream.Read(data, 0, datasize);
        }

        public void SaveCode(object Para)
        {
            SaveCodePara p=(SaveCodePara)Para;
            SavingCode(this, EventArgs.Empty);
            using (FileStream fs = File.Create((p.Path)))
            {
                using (MemoryStream ms = new MemoryStream(data))
                {
                    using (BinaryReader mr = new BinaryReader(ms))
                    {
                        ms.Position = Header.HeaderEntries[0].offset;
                        if (!p.IsCompressed)
                            fs.Write(mr.ReadBytes((int)Header.HeaderEntries[0].size), 0, (int)Header.HeaderEntries[0].size);
                        else
                        {
                            DecompressingCode(this, EventArgs.Empty);
                            long DecompressedSize = BLZ.GetDecompressedSize(ms, (int)Header.HeaderEntries[0].size);
                            byte[] Decompressed = BLZ.Decompress(ms, (int)Header.HeaderEntries[0].size, DecompressedSize);
                            fs.Write(Decompressed, 0, (int)DecompressedSize);
                            DecompressedCode(this, EventArgs.Empty);
                        }
                    }
                }
            }
            SavedCode(this, EventArgs.Empty);
        }
        public void SaveBanner(string path)
        {
            using (FileStream fs = File.Create(path))
            {
                using (MemoryStream ms = new MemoryStream(data))
                {
                    using (BinaryReader mr = new BinaryReader(ms))
                    {
                        ms.Position = Header.HeaderEntries[1].offset;
                        fs.Write(mr.ReadBytes((int)Header.HeaderEntries[1].size), 0, (int)Header.HeaderEntries[2].size);
                    }
                }
            }
        }
        public void SaveIcon(string path)
        {
            using (FileStream fs = File.Create(path))
            {
                using (MemoryStream ms = new MemoryStream(data))
                {
                    using (BinaryReader mr = new BinaryReader(ms))
                    {
                        ms.Position = Header.HeaderEntries[2].offset;
                        fs.Write(mr.ReadBytes((int)Header.HeaderEntries[2].size), 0, (int)Header.HeaderEntries[2].size);
                    }
                }
            }
        }

        public delegate void CodeSavingHandler(object sender, EventArgs e);
        public delegate void CodeSavedHandler(object sender, EventArgs e);
        public delegate void CodeDecompressingHandler(object sender, EventArgs e);
        public delegate void CodeDecompressedHandler(object sender, EventArgs e);
        public event CodeSavingHandler SavingCode;
        public event CodeSavedHandler SavedCode;
        public event CodeDecompressingHandler DecompressingCode;
        public event CodeDecompressedHandler DecompressedCode;
    }
    public class SaveCodePara
    {
        public string Path;
        public bool IsCompressed;
        public SaveCodePara(string path,bool iscompressed)
        {
            Path = path;
            IsCompressed = iscompressed;
        }
    }
}
